package base

import (
	"context"
	"github.com/zeromicro/go-zero/core/logx"
	"payme3000/framework/config_center"
	"payme3000/framework/errorx"
	"payme3000/framework/util/array"
	"reflect"
	"sync"
)

const (
	ModuleTypeRequired ModuleType = iota
	ModuleTypeCache
	ModuleTypeDispatcher
	ModuleTypeManager
	ModuleTypeProducer
	ModuleTypeController
)

type (
	// IApplication 服务运行时载体
	// 整个服务运行期间实际上都在维护Application的运行周期
	// Application提供容器功能和注入功能
	// 负责维护所有注册进来的业务逻辑模块, 但注意只负责调用Module interface提供的方法
	// 服务可以提供多个实现了ICache/IDispatcher/IManager/IProducer等接口的Module, 但IConfig/ILogic/IRepository/IServer有且仅有一个
	IApplication interface {
		IComponent
		ISetAppConf
		ISetConfigCenter
		placeholderApplication()
	}

	// Application IApplication的基础实现
	Application struct {
		Component

		// public
		// customized modules
		ModuleRegister []IModule // 要注册的模块

		// private
		// required modules
		cfg    IConfig     // 服务的业务配置
		repo   IRepository // 服务的资源访问
		logic  ILogic      // 服务的业务逻辑入口
		server IServer     // 服务的server功能

		// 默认依赖项
		appConf      *AppConf                    // 程序启动配置, 必须依赖
		configCenter *config_center.ConfigCenter // 配置中心, 可设置不依赖, 一般用于完全本地运行的服务

		runningModuleMap map[ModuleType][]IModule // 存放Start成功的running module

		ready chan struct{} // 就绪标记

		stopOnce sync.Once
	}

	// ModuleType 注册进Application的模块类型
	ModuleType int

	// ISetHandleStop 实现该接口的模块具备stop app的能力
	ISetHandleStop interface {
		SetHandleStop(func())
	}

	// ISetAppReady 实现该接口的模块能收到app就绪信息
	ISetAppReady interface {
		SetAppReady(chan struct{})
	}
)

func (a *Application) Init(ctx context.Context) {
	a.Component.Init(ctx)
	a.runningModuleMap = map[ModuleType][]IModule{}
	a.ready = make(chan struct{})
}

func (a *Application) Start() error {
	// Application的启动分为以下阶段
	// 阶段1 前置作业
	// 阶段2 注册模块
	// 阶段3 检查模块
	// 阶段4 构建依赖
	// 阶段5 启动模块&模块注入
	// 阶段6 后置作业

	// 阶段1 前置作业
	logx.Infof("ready to start app")

	// 阶段2 注册模块
	// 注意, 所有外部注册进来的模块都要检查是否为nil
	customizedModuleMap := map[ModuleType][]IModule{}
	deWeightModuleMap := map[IModule]struct{}{} // 仅去重用
	for _, m := range a.ModuleRegister {
		if m == nil {
			continue
		}

		// 去重
		if _, ok := deWeightModuleMap[m]; ok {
			continue
		} else {
			deWeightModuleMap[m] = struct{}{}
		}

		// 模块分门别类
		switch _m := m.(type) {
		// 继承内置模块的模块, 转换类型时要用interface
		case IConfig:
			a.cfg = _m
		case IRepository:
			a.repo = _m
		case ILogic:
			a.logic = _m
		case IServer:
			a.server = _m
		case ICache:
			customizedModuleMap[ModuleTypeCache] = append(customizedModuleMap[ModuleTypeCache], m)
		case IDispatcher:
			customizedModuleMap[ModuleTypeDispatcher] = append(customizedModuleMap[ModuleTypeDispatcher], m)
		case IManager:
			customizedModuleMap[ModuleTypeManager] = append(customizedModuleMap[ModuleTypeManager], m)
		case IProducer:
			customizedModuleMap[ModuleTypeProducer] = append(customizedModuleMap[ModuleTypeProducer], m)
		case IController:
			customizedModuleMap[ModuleTypeController] = append(customizedModuleMap[ModuleTypeController], m)
		default:
			_ = errorx.Errorf(context.Background(), "register illegal module with %s", reflect.TypeOf(m).Elem().String())
		}

		logx.Infof("register module with %s", reflect.TypeOf(m).Elem().String())
	}

	// 阶段3 检查模块
	// 必须提供config模块
	if a.cfg == nil {
		return errorx.New(context.Background(), "config module not found")
	}

	// 必须提供logic模块
	if a.logic == nil {
		return errorx.New(context.Background(), "logic module not found")
	}

	// 必须提供repository模块
	if a.repo == nil {
		return errorx.New(context.Background(), "repo module not found")
	}

	// 必须提供server模块
	if a.server == nil {
		return errorx.New(context.Background(), "server module not found")
	}

	// 阶段4 构建依赖

	// 阶段5 启动模块&模块注入
	// 执行模块的Init和Start
	// 顺序敏感
	// config优先级最高, 确保配置完好才继续下去
	// server优先级最低, 确保其他module妥当后再启动server开放对外接口
	readyToStartModuleList := []IModule{
		a.cfg,
		a.repo,
	}
	readyToStartModuleList = append(readyToStartModuleList, customizedModuleMap[ModuleTypeCache]...)
	readyToStartModuleList = append(readyToStartModuleList, customizedModuleMap[ModuleTypeDispatcher]...)
	readyToStartModuleList = append(readyToStartModuleList, customizedModuleMap[ModuleTypeProducer]...)
	readyToStartModuleList = append(readyToStartModuleList, customizedModuleMap[ModuleTypeManager]...)
	readyToStartModuleList = append(readyToStartModuleList, customizedModuleMap[ModuleTypeController]...)
	readyToStartModuleList = append(readyToStartModuleList, a.logic, a.server)
	for _, m := range readyToStartModuleList {
		moduleRefName := reflect.TypeOf(m).Elem().String()

		// 每个模块Start都成功后, Application才会被认为正常启动
		// 先执行Init, 传递Application的context
		// 模块默认的Init会继承Application的context, 除非自定义Init可选择不继承
		m.Init(a.Ctx)
		logx.Infof("init module with %s", moduleRefName)

		// 处理注入, 能转换成功即认为需要注入对应的实例
		// 注入 AppConf
		if _m, ok := m.(ISetAppConf); ok {
			if a.appConf == nil {
				return errorx.Throw(context.Background(), "required AppConf but not provided")
			} else {
				_m.SetAppConf(a.appConf)
				logx.Infof("inject AppConf to %s", moduleRefName)
			}
		}

		// 注入 ConfigCenter
		if _m, ok := m.(ISetConfigCenter); ok {
			if a.configCenter == nil {
				// repo和logic默认依赖ConfigCenter, 如不提供ConfigCenter, 基于repo和logic模块的功能要注意
				//if m != a.repo && m != a.logic {
				//	return errorx.Throw(context.Background(), "required ConfigCenter but not provided")
				//}
			} else {
				_m.SetConfigCenter(a.configCenter)
				logx.Infof("inject ConfigCenter to %s", moduleRefName)
			}
		}

		// 注入 HandleStop
		if _m, ok := m.(ISetHandleStop); ok {
			_m.SetHandleStop(a.HandleStop)
			logx.Infof("inject HandleStop to %s", moduleRefName)
		}

		// 注入 AppReady
		if _m, ok := m.(ISetAppReady); ok {
			_m.SetAppReady(a.ready)
			logx.Infof("inject AppReady to %s", moduleRefName)
		}

		// 执行Start, 注意要先注入再Start
		if err := m.Start(); err != nil {
			logx.Errorf("start module error with %s", moduleRefName)
			return err
		} else {
			// 需要保存成功Start的模块, 以便Application执行Stop时略过未Start的模块
			// 通常是Application启动到一半有抛出error的模块导致服务就此中断
			// 此时正常停止服务依然会依次执行已成功注册的模块以保证graceful stop
			// 模块分门别类
			switch m.(type) {
			case IConfig:
				a.runningModuleMap[ModuleTypeCache] = append(a.runningModuleMap[ModuleTypeCache], m)
			case IDispatcher:
				a.runningModuleMap[ModuleTypeDispatcher] = append(a.runningModuleMap[ModuleTypeDispatcher], m)
			case IProducer:
				a.runningModuleMap[ModuleTypeProducer] = append(a.runningModuleMap[ModuleTypeProducer], m)
			case IManager:
				a.runningModuleMap[ModuleTypeManager] = append(a.runningModuleMap[ModuleTypeManager], m)
			case IController:
				a.runningModuleMap[ModuleTypeController] = append(a.runningModuleMap[ModuleTypeController], m)
			default:
				a.runningModuleMap[ModuleTypeRequired] = append(a.runningModuleMap[ModuleTypeRequired], m)
			}

			logx.Infof("start module with %s", moduleRefName)
		}
	}

	// 阶段6 后置作业
	// 设置就绪标记, 注意仅能close一次
	close(a.ready)

	logx.Infof("app start done")

	return nil
}

func (a *Application) Stop() {
	a.stopOnce.Do(func() {
		logx.Info("ready to stop app")

		// 需要stop所有running的模块
		// 每个模块按需自行实现各自的graceful stop
		// 注意必须判断是否在runningModuleMap里
		// 顺序敏感, 需要最先stop入口模块, 以确保不再有新的请求调用
		var readyToStopModuleList []IModule
		// stop server/logic
		for _, m := range []IModule{
			a.server,
			a.logic,
		} {
			if array.In(m, a.runningModuleMap[ModuleTypeRequired]) {
				readyToStopModuleList = append(readyToStopModuleList, m)
			}
		}
		// stop customized
		for _, mt := range []ModuleType{
			ModuleTypeController,
			ModuleTypeManager,
			ModuleTypeProducer,
			ModuleTypeDispatcher,
			ModuleTypeCache,
		} {
			l := len(a.runningModuleMap[mt])
			if l > 0 {
				for i := range a.runningModuleMap[mt] {
					c := a.runningModuleMap[mt][l-i-1]
					readyToStopModuleList = append(readyToStopModuleList, c)
				}
			}
		}
		// stop repo/cfg
		for _, m := range []IModule{
			a.repo,
			a.cfg,
		} {
			if array.In(m, a.runningModuleMap[ModuleTypeRequired]) {
				readyToStopModuleList = append(readyToStopModuleList, m)
			}
		}

		// 依次stop module
		for _, m := range readyToStopModuleList {
			m.Stop()
			logx.Infof("stop module with %s", reflect.TypeOf(m).Elem().String())
		}

		// 最后停止config center
		if a.configCenter != nil {
			a.configCenter.Stop()
		}

		// 覆盖了默认的Stop, 需手动执行默认Stop
		a.Component.Stop()

		logx.Info("app stop done")
	})
}

func (a *Application) SetAppConf(appConf *AppConf) {
	a.appConf = appConf
}

func (a *Application) SetConfigCenter(configCenter *config_center.ConfigCenter) {
	a.configCenter = configCenter
}

// HandleStop 供其他Module stop Application
func (a *Application) HandleStop() {
	a.Stop()
}

func (a *Application) placeholderApplication() {}
